/* $Id: parser.yy 48 2009-09-05 08:07:10Z tb $ -*- mode: c++ -*- */
/** \file parser.yy Contains the example Bison parser source */

%{ /*** C/C++ Declarations ***/

#include <stdio.h>
#include <string>
#include <vector>

#include "tree/ArrayAccess.h"
#include "tree/BinaryOperation.h"
#include "tree/ConstantValue.h"
#include "tree/FieldAccess.h"
#include "tree/UnaryOperation.h"
#include "tree/Variable.h"

#include "tree/ClassDefinition.h"
#include "tree/FunctionDefinition.h"
#include "tree/MethodDefinition.h"

#include "tree/Assign.h"
#include "tree/EmptyStatement.h"
#include "tree/ForLoop.h"
#include "tree/IfStatement.h"
#include "tree/NewExpression.h"
#include "tree/ReturnStatement.h"
#include "tree/StatementList.h"
#include "tree/VarDeclaration.h"
#include "tree/WhileLoop.h"

#include "tree/FunctionCall.h"
#include "tree/MethodCall.h"

#include "ParseHelper.h"
%}

/*** yacc/bison Declarations ***/

/* Require bison 2.3 or later */
%require "2.3"

/* add debug output code to generated parser. disable this for release
 * versions. */
%debug

/* start symbol is named "start" */
%start translation_unit

/* write out a header file containing the token defines */
%defines

/* use newer C++ skeleton file */
%skeleton "lalr1.cc"

/* namespace to enclose parser in */
%name-prefix="RUN"

/* set the parser's class identifier */
%define "parser_class_name" "Parser"

/* keep track of the current position within the input */
%locations
%initial-action
{
    // initialize the initial location object
    @$.begin.filename = @$.end.filename = &driver.streamname;
};

/* The driver is passed by reference to the parser and to the scanner. This
 * provides a simple but effective pure interface, not relying on global
 * variables. */
%parse-param { class Driver& driver }

/* verbose error messages */
%error-verbose

 /*** BEGIN EXAMPLE - Change the example grammar's tokens below ***/

//%token IDENTIFIER CONSTANT STRING_LITERAL
%token INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
%token XOR_ASSIGN OR_ASSIGN

%token CLASS METHOD VAR THIS NEW FUNCTION

%token SEMICOLON COMMA LPAREN RPAREN LBRACE RBRACE LBRACKET RBRACKET DOT COLON
%token BIT_AND_OP BIT_OR_OP XOR_OP NOT_OP NEG_OP ASSIGN LT_OP GT_OP MINUS_OP PLUS_OP MUL_OP DIV_OP MOD_OP

%token IF ELSE WHILE DO FOR CONTINUE BREAK RETURN

%token END 0 "end of file"

%union {
    long            integerVal;
    double          doubleVal;
    std::string*    stringVal;
    class TreeNode* node;
}

%token <integerVal> 	CONSTANT		"integer"
%token <doubleVal> 	DOUBLE		"double"
%token <stringVal> 	STRING_LITERAL		"string_literal"
%token <stringVal> 	IDENTIFIER		"identifier"

%type <node>	additive_expression array_access assignment_expression class_definition compound_statement conditional_expression
%type <node>  equality_expression expression expression_statement function_definition iteration_statement jump_statement logical_and_expression
%type <node>  logical_or_expression method_definition multiplicative_expression postfix_expression primary_expression relational_expression
%type <node>  selection_statement statement unary_expression var_declaration var_declarations field_declaration_list field_access 

 /*** END EXAMPLE - Change the example grammar's tokens above ***/

%{

#include "Driver.h"
#include "scanner.h"

/* this "connects" the bison parser in the driver to the flex scanner class
 * object. it defines the yylex() function call to pull the next token from the
 * current lexer object of the driver context. */
#undef yylex
#define yylex driver.lexer->lex

%}

%% /*** Grammar Rules ***/

 /*** BEGIN EXAMPLE - Change the example grammar rules below ***/

primary_expression
        : IDENTIFIER
			{
				$$ = new Variable(*$1);
				delete $1;
			}
        | CONSTANT
			{
				$$ = new ConstantValue($1);
			}
        | STRING_LITERAL
        	{
        	driver.helper.SanitizeString(&$1);
				$$ = new ConstantValue($1);
				driver.helper.AddStringConstant($1);
			}
        | LPAREN expression RPAREN
        	{
        		$$ = $2;
        	}
        /*| THIS
			{
				$$ = new Variable($1);
			}*/
        ;

field_access:
        postfix_expression DOT IDENTIFIER
        {
          $$ = new FieldAccess($1, *$3);
				  delete $3;
        }
        ;

array_access:
      postfix_expression LBRACKET additive_expression RBRACKET
    	{
    		$$ = new ArrayAccess($1, $3);
    	}
      ;

postfix_expression
        : primary_expression
        	{
        		$$ = $1;
        	}
        | array_access
          {
            $$ = $1;
          }
        /*| postfix_expression LPAREN RPAREN
        	{
        		$$ = new FunctionCall(*$1, $3);
        	}*/
        | IDENTIFIER LPAREN { driver.helper.NewCallContext(); } argument_expression_list RPAREN
        	{
        	  driver.helper.AddCalledFunctionName(*$1);
        		$$ = new FunctionCall(*$1, driver.helper.GetCallParams());
				    delete $1;
        	}
        //| postfix_expression DOT IDENTIFIER LPAREN RPAREN // mrknout na tohle

        | postfix_expression DOT IDENTIFIER LPAREN { driver.helper.NewCallContext(); } argument_expression_list RPAREN // ''
        	{
        		$$ = new MethodCall($1, *$3, driver.helper.GetCallParams());
        		delete $3;
        	}
        //| postfix_expression INC_OP
        //| postfix_expression DEC_OP
        | NEW IDENTIFIER LPAREN { driver.helper.NewCallContext(); } argument_expression_list RPAREN
        	{
        		$$ = new NewExpression(*$2, driver.helper.GetCallParams());
				    delete $2;
        	}
        | field_access
        {
          $$ = $1;
        }
        ;

argument_expression_list
		:
		| conditional_expression
			{
				driver.helper.AddCallParam($1);
			}
		| argument_expression_list COMMA conditional_expression
			{
				driver.helper.AddCallParam($3);
			}
        ;

unary_expression
        : postfix_expression
        	{
        		$$ = $1;
        	}
        //| INC_OP unary_expression
        //| DEC_OP unary_expression
        | MINUS_OP unary_expression
			{
        		$$ = new UnaryOperation($2, UnaryMinus);
        	}
        | NOT_OP unary_expression
        	{
        		$$ = new UnaryOperation($2, Negation);
        	}
        ;

multiplicative_expression
        : unary_expression
        	{
        		$$ = $1;
        	}
        | multiplicative_expression MUL_OP unary_expression
        	{
        		$$ = new BinaryOperation(Multiply, $1, $3);
        	}
        | multiplicative_expression DIV_OP unary_expression
        	{
        		$$ = new BinaryOperation(Divide, $1, $3);
        	}
        | multiplicative_expression MOD_OP unary_expression
        	{
        		$$ = new BinaryOperation(Modulo, $1, $3);
        	}
        ;

additive_expression
        : multiplicative_expression
        	{
        		$$ = $1;
        	}
        | additive_expression PLUS_OP multiplicative_expression
        	{
        		$$ = new BinaryOperation(Plus, $1, $3);
        	}
        | additive_expression MINUS_OP multiplicative_expression
        	{
        		$$ = new BinaryOperation(Minus, $1, $3);
        	}
        ;

relational_expression
        : additive_expression
        	{
        		$$ = $1;
        	}
        | relational_expression LT_OP additive_expression
        	{
        		$$ = new BinaryOperation(Less, $1, $3);
        	}
        | relational_expression GT_OP additive_expression
        	{
        		$$ = new BinaryOperation(Greater, $1, $3);
        	}
        | relational_expression LE_OP additive_expression
        	{
        		$$ = new BinaryOperation(LessOrEqual, $1, $3);
        	}
        | relational_expression GE_OP additive_expression
        	{
        		$$ = new BinaryOperation(GreaterOrEqual, $1, $3);
        	}
        ;

equality_expression
        : relational_expression
        	{
        		$$ = $1;
        	}
        | equality_expression EQ_OP relational_expression
        	{
        		$$ = new BinaryOperation(Equal, $1, $3);
        	}
        | equality_expression NE_OP relational_expression
        	{
        		$$ = new BinaryOperation(NotEqual, $1, $3);
        	}
        ;

logical_and_expression
        : equality_expression
        	{
        		$$ = $1;
        	}
        | logical_and_expression AND_OP equality_expression
        	{
        		$$ = new BinaryOperation(And, $1, $3);
        	}
        ;

logical_or_expression
        : logical_and_expression
        	{
        		$$ = $1;
        	}
        | logical_or_expression OR_OP logical_and_expression
        	{
        		$$ = new BinaryOperation(Or, $1, $3);
        	}
        ;

conditional_expression
        : logical_or_expression
        	{
        		$$ = $1;
        	}
        ;

assignment_expression
        : IDENTIFIER ASSIGN conditional_expression
        	{
        		$$ = new Assign(new Variable(*$1), $3);
				    delete $1;
        	}
        | field_access ASSIGN conditional_expression
          {
        		$$ = new Assign($1, $3);
          }
        | array_access ASSIGN conditional_expression
          {
            $$ = new Assign($1, $3);
          }
        ;

expression
        : conditional_expression
        	{
        		$$ = $1;
        	}
        ;

var_declarations
		: VAR var_declaration_list SEMICOLON
			{
				//$$ = driver.helper.GetVarDeclarations();
                $$ = new StatementList(driver.helper.GetVarDeclarations());
			}
		;

var_declaration_list
		: var_declaration
			/*{
				driver.helper.AddVar($1);
			}*/
		| var_declaration_list COMMA var_declaration
			/*{
				driver.helper.AddVar($3);
			}*/
		;

var_declaration
		: IDENTIFIER
			{
				driver.helper.AddVar(new VarDeclaration(*$1));
				delete $1;
			}
		| IDENTIFIER ASSIGN conditional_expression
      {
        driver.helper.AddVar(new VarDeclaration(*$1, $3));
				delete $1;
      }
		;

parameter_list
        :
		    | IDENTIFIER
          {
            driver.helper.AddDefinitionParameter(new Variable(*$1));
				    delete $1;
          }
        | parameter_list COMMA IDENTIFIER
          {
				    driver.helper.AddDefinitionParameter(new Variable(*$3));
				    delete $3;
          }
        ;

inheritance_list
	    : IDENTIFIER
			     {
				      driver.helper.AddBaseClass(*$1);
				      delete $1;
			     }
        | inheritance_list COMMA IDENTIFIER
        	{
				    driver.helper.AddBaseClass(*$3);
				    delete $3;
			     }
		    ;

statement
        : var_declarations
        	{
        		$$ = $1;
        	}
        | compound_statement
        	{
        		$$ = $1;
        	}
        | expression_statement
        	{
        		$$ = $1;
        	}
        | selection_statement
        	{
        		$$ = $1;
        	}
        | iteration_statement
        	{
        		$$ = $1;
        	}
        | jump_statement
        	{
        		$$ = $1;
        	}
        ;


compound_statement
        : LBRACE RBRACE
        	{
        		$$ = new EmptyStatement();
        	}
        | LBRACE { driver.helper.NewCompStmtContext(); } statement_list RBRACE
        	{
        		$$ = new StatementList(driver.helper.GetStatements());
        	}
        ;

statement_list
        : statement
        	{
        		driver.helper.AddStatement($1);
        	}
        | statement_list statement
        	{
        		driver.helper.AddStatement($2);
        	}
        ;

expression_statement
        : expression SEMICOLON
        	{
        		$$ = $1;
        	}
        | assignment_expression SEMICOLON
        	{
        		$$ = $1;
        	}
        ;

selection_statement
        : IF LPAREN expression RPAREN statement
        	{
        		$$ = new IfStatement($3, $5);
        	}
        | IF LPAREN expression RPAREN statement ELSE statement
        	{
        		$$ = new IfStatement($3, $5, $7);
        	}
        ;

iteration_statement
        : WHILE LPAREN expression RPAREN statement
        	{
        		$$ = new WhileLoop($3, $5);
        	}
        //| DO statement WHILE LPAREN expression RPAREN SEMICOLON
        //| FOR LPAREN assignment_expression SEMICOLON expression_statement RPAREN statement
        | FOR LPAREN assignment_expression SEMICOLON expression SEMICOLON assignment_expression RPAREN statement
        	{
        		$$ = new ForLoop($3, $5, $9, $7);
        	}
        ;

jump_statement
        /*: CONTINUE SEMICOLON
        | BREAK SEMICOLON*/
        : RETURN SEMICOLON
        	{
        		$$ = new ReturnStatement(NULL);
        	}
        | RETURN expression SEMICOLON
        	{
        		$$ = new ReturnStatement($2);
        	}
        ;

translation_unit
        : external_declaration
        | translation_unit external_declaration
        ;

external_declaration
        : function_definition
        	{
        		//driver.helper.AddFunction($1);
        	}
        | class_definition
        	{
        		//driver.helper.AddClass($1);
        	}
        ;

function_definition
        : FUNCTION IDENTIFIER LPAREN parameter_list RPAREN compound_statement
            {
                driver.helper.AddFunction(new FunctionDefinition(*$2, driver.helper.GetDefinitonParams(), $6));
				        delete $2;
            }
        /*| FUNCTION IDENTIFIER LPAREN RPAREN compound_statement
            {
                $$ = new FunctionDefinition($2, NULL, $6)
            }*/
        ;
        
method_definition
        : METHOD IDENTIFIER LPAREN parameter_list RPAREN compound_statement
            {
                driver.helper.AddMethod(new MethodDefinition(*$2, driver.helper.GetDefinitonParams(), $6));
				        delete $2;
            }
        /*| METHOD IDENTIFIER LPAREN RPAREN compound_statement
            {
                $$ = new MethodDefinition($2, , $6);
            }*/
        ;

class_definition
        : CLASS IDENTIFIER COLON inheritance_list LBRACE in_class_declaration_list RBRACE
            {
                driver.helper.AddClass(new ClassDefinition(*$2, driver.helper.GetFields(), driver.helper.GetMethods(), driver.helper.GetBaseClasses()));
				        delete $2;
            }
        | CLASS IDENTIFIER LBRACE in_class_declaration_list RBRACE
            {
                driver.helper.AddClass(new ClassDefinition(*$2, driver.helper.GetFields(), driver.helper.GetMethods(), driver.helper.GetBaseClasses()));
				        delete $2;
            }
        ;

in_class_declaration_list
        : in_class_declaration
        | in_class_declaration_list in_class_declaration
        ;

in_class_declaration
        : method_definition
            /*{
                driver.helper.AddMethod($1);
            }*/
        | VAR field_declaration_list SEMICOLON
        ;

field_declaration_list
		: IDENTIFIER
			{
				driver.helper.AddField(*$1);
				delete $1;
			}
		| field_declaration_list COMMA IDENTIFIER
			{
				driver.helper.AddField(*$3);
				delete $3;
			}
		;

 /*** END EXAMPLE - Change the example grammar rules above ***/

%% /*** Additional Code ***/

void RUN::Parser::error(const Parser::location_type& l,
			    const std::string& m)
{
    driver.error(l, m);
}
